/*-------------------------------------------------------------------------
 *
 * lock.h
 *	  POSTGRES 低级锁机制
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/storage/lock.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef LOCK_H_
#define LOCK_H_

#ifdef FRONTEND
#error "lock.h may not be included from frontend code"
#endif

#include "storage/backendid.h"
#include "storage/lockdefs.h"
#include "storage/lwlock.h"
#include "storage/shmem.h"
#include "utils/timestamp.h"

/* struct PGPROC 在 proc.h 中声明，但必须前向引用它 */
typedef struct PGPROC PGPROC;

typedef struct PROC_QUEUE
{
	SHM_QUEUE	links;			/* PGPROC 对象的列表头 */
	int			size;			/* 列表中的条目数 */
} PROC_QUEUE;

/* GUC 变量 */
extern PGDLLIMPORT int max_locks_per_xact;

#ifdef LOCK_DEBUG
extern PGDLLIMPORT int Trace_lock_oidmin;
extern PGDLLIMPORT bool Trace_locks;
extern PGDLLIMPORT bool Trace_userlocks;
extern PGDLLIMPORT int Trace_lock_table;
extern PGDLLIMPORT bool Debug_deadlocks;
#endif							/* LOCK_DEBUG */


/*
 * 顶级事务通过由 PGPROC 字段 backendId 和 lxid 组成的 VirtualTransactionIDs 进行识别。
 * 对于恢复的准备事务，LocalTransactionId 是普通的 XID；LOCKTAG_VIRTUALTRANSACTION
 * 从不引用那种。 这些在短期内保证唯一，但
 * 在数据库重启或 XID 回绕后将被重用；因此它们
 * 不应存储在磁盘上。
 *
 * 注意，struct VirtualTransactionId 不能假定整个原子赋值。
 * 然而，类型 LocalTransactionId 被假定为
 * 原子赋值，并且后端 ID 变化不够频繁，
 * 因此我们可以单独获取或赋值这两个字段。
 * 我们故意避免在 PGPROC 内部使用该结构，以防止
 * 试图使用结构赋值时出现编码错误；而是使用
 * GET_VXID_FROM_PGPROC()。
 */
typedef struct
{
	BackendId	backendId;		/* PGPROC 中的 backendId */
	LocalTransactionId localTransactionId;	/* PGPROC 中的 lxid */
} VirtualTransactionId;

#define InvalidLocalTransactionId		0
#define LocalTransactionIdIsValid(lxid) ((lxid) != InvalidLocalTransactionId)
#define VirtualTransactionIdIsValid(vxid) \
	(LocalTransactionIdIsValid((vxid).localTransactionId))
#define VirtualTransactionIdIsRecoveredPreparedXact(vxid) \
	((vxid).backendId == InvalidBackendId)
#define VirtualTransactionIdEquals(vxid1, vxid2) \
	((vxid1).backendId == (vxid2).backendId && \
	 (vxid1).localTransactionId == (vxid2).localTransactionId)
#define SetInvalidVirtualTransactionId(vxid) \
	((vxid).backendId = InvalidBackendId, \
	 (vxid).localTransactionId = InvalidLocalTransactionId)
#define GET_VXID_FROM_PGPROC(vxid, proc) \
	((vxid).backendId = (proc).backendId, \
	 (vxid).localTransactionId = (proc).lxid)

/* MAX_LOCKMODES 不能大于 LOCKMASK 中的位数 */
#define MAX_LOCKMODES		10

#define LOCKBIT_ON(lockmode) (1 << (lockmode))
#define LOCKBIT_OFF(lockmode) (~(1 << (lockmode)))


/*
 * 该数据结构定义与“锁定方法”相关的锁定语义。
 * 语义指定每个锁定模式的含义（通过定义它与哪些锁定模式冲突）。
 * 所有这些数据都是常量，并保存在常量表中。
 *
 * numLockModes -- 在此锁定方法中定义的锁定模式（READ, WRITE 等）的数量。
 * 必须小于 MAX_LOCKMODES。
 *
 * conflictTab -- 这是一个位掩码数组，显示锁定
 * 模式冲突。 conflictTab[i] 是一个掩码，如果锁定模式 i 和 j 冲突，则第 j 位
 * 被打开。锁定模式从 1..numLockModes 编号；conflictTab[0] 未使用。
 *
 * lockModeNames -- 用于调试输出的 ID 字符串。
 *
 * trace_flag -- 指向此锁定方法的 GUC 跟踪标志的指针。 （GUC 变量不是常量，
 * 但我们在这里使用“const”来表示它不能通过此引用更改。）
 */
typedef struct LockMethodData
{
	int			numLockModes;
	const LOCKMASK *conflictTab;
	const char *const *lockModeNames;
	const bool *trace_flag;
} LockMethodData;

typedef const LockMethodData *LockMethod;

/*
 * 锁定方法由 LOCKMETHODID 识别。 （尽管声明为
 * uint16，但由于 LOCKTAG 的布局，我们被限制为 256 种锁定方法。）
 */
typedef uint16 LOCKMETHODID;

/* 这些标识已知的锁定方法 */
#define DEFAULT_LOCKMETHOD	1
#define USER_LOCKMETHOD		2

/*
 * LOCKTAG 是在锁定哈希表中查找 LOCK 项所需的关键信息。
 * LOCKTAG 值唯一标识一个可锁定对象。
 *
 * LockTagType 枚举定义了我们可以锁定的不同类型的对象。
 * 我们最多可以处理 256 种不同的 LockTagTypes。
 */
typedef enum LockTagType
{
	LOCKTAG_RELATION,			/* 整个关系 */
	LOCKTAG_RELATION_EXTEND,	/* 扩展关系的权利 */
	LOCKTAG_DATABASE_FROZEN_IDS,	/* pg_database.datfrozenxid */
	LOCKTAG_PAGE,				/* 一个关系的一页 */
	LOCKTAG_TUPLE,				/* 一个物理元组 */
	LOCKTAG_TRANSACTION,		/* 事务（用于等待事务完成） */
	LOCKTAG_VIRTUALTRANSACTION, /* 虚拟事务（同上） */
	LOCKTAG_SPECULATIVE_TOKEN,	/* 推测插入的 Xid 和 token */
	LOCKTAG_OBJECT,				/* 非关系数据库对象 */
	LOCKTAG_USERLOCK,			/* 为旧的 contrib/userlock 代码保留 */
	LOCKTAG_ADVISORY			/* 顾问用户锁 */
} LockTagType;

#define LOCKTAG_LAST_TYPE	LOCKTAG_ADVISORY

extern PGDLLIMPORT const char *const LockTagTypeNames[];

/*
 * LOCKTAG 结构被故意设计为适合 16
 * 字节而没有填充。 注意，如果我们将 Oid、BlockNumber 或
 * TransactionId 扩展到超过 32 位，则需要进行调整。
 *
 * 我们在锁定标签中包含 lockmethodid，以便单个共享内存中的哈希表可以存储不同锁定方法的锁定。
 */
typedef struct LOCKTAG
{
	uint32		locktag_field1; /* 一个 32 位 ID 字段 */
	uint32		locktag_field2; /* 一个 32 位 ID 字段 */
	uint32		locktag_field3; /* 一个 32 位 ID 字段 */
	uint16		locktag_field4; /* 一个 16 位 ID 字段 */
	uint8		locktag_type;	/* 查看枚举 LockTagType */
	uint8		locktag_lockmethodid;	/* 锁方法指示符 */
} LOCKTAG;

/*
 * 这些宏定义了我们如何将可锁对象的逻辑 ID 映射到
 * LOCKTAG 的物理字段。使用这些宏来设置 LOCKTAG 值，
 * 而不是直接访问字段。注意目标的多次求值！
 */

/* 关系的 ID 信息为 DB OID + REL OID；当共享时 DB OID = 0 */
#define SET_LOCKTAG_RELATION(locktag,dboid,reloid) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = (reloid), \
	 (locktag).locktag_field3 = 0, \
	 (locktag).locktag_field4 = 0, \
	 (locktag).locktag_type = LOCKTAG_RELATION, \
	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)

/* 与 RELATION 相同的 ID 信息 */
#define SET_LOCKTAG_RELATION_EXTEND(locktag,dboid,reloid) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = (reloid), \
	 (locktag).locktag_field3 = 0, \
	 (locktag).locktag_field4 = 0, \
	 (locktag).locktag_type = LOCKTAG_RELATION_EXTEND, \
	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)

/* 冻结 ID 的 ID 信息为 DB OID */
#define SET_LOCKTAG_DATABASE_FROZEN_IDS(locktag,dboid) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = 0, \
	 (locktag).locktag_field3 = 0, \
	 (locktag).locktag_field4 = 0, \
	 (locktag).locktag_type = LOCKTAG_DATABASE_FROZEN_IDS, \
	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)

/* 页面 ID 信息为 RELATION 信息 + BlockNumber */
#define SET_LOCKTAG_PAGE(locktag,dboid,reloid,blocknum) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = (reloid), \
	 (locktag).locktag_field3 = (blocknum), \
	 (locktag).locktag_field4 = 0, \
	 (locktag).locktag_type = LOCKTAG_PAGE, \
	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)

/* 元组 ID 信息为 PAGE 信息 + OffsetNumber */
#define SET_LOCKTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = (reloid), \
	 (locktag).locktag_field3 = (blocknum), \
	 (locktag).locktag_field4 = (offnum), \
	 (locktag).locktag_type = LOCKTAG_TUPLE, \
	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)

/* 事务 ID 信息为其 TransactionId */
#define SET_LOCKTAG_TRANSACTION(locktag,xid) \
	((locktag).locktag_field1 = (xid), \
	 (locktag).locktag_field2 = 0, \
	 (locktag).locktag_field3 = 0, \
	 (locktag).locktag_field4 = 0, \
	 (locktag).locktag_type = LOCKTAG_TRANSACTION, \
	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)

/* 虚拟事务 ID 信息为其 VirtualTransactionId */
#define SET_LOCKTAG_VIRTUALTRANSACTION(locktag,vxid) \
	((locktag).locktag_field1 = (vxid).backendId, \
	 (locktag).locktag_field2 = (vxid).localTransactionId, \
	 (locktag).locktag_field3 = 0, \
	 (locktag).locktag_field4 = 0, \
	 (locktag).locktag_type = LOCKTAG_VIRTUALTRANSACTION, \
	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)

/*
 * 给一个推测性插入的 ID 信息是 TRANSACTION 信息 +
 * 其推测性插入计数器。
 */
#define SET_LOCKTAG_SPECULATIVE_INSERTION(locktag,xid,token) \
	((locktag).locktag_field1 = (xid), \
	 (locktag).locktag_field2 = (token),		\
	 (locktag).locktag_field3 = 0, \
	 (locktag).locktag_field4 = 0, \
	 (locktag).locktag_type = LOCKTAG_SPECULATIVE_TOKEN, \
	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)

/*
 * 对象的 ID 信息是 DB OID + CLASS OID + OBJECT OID + SUBID
 *
 * 注意：对象 ID 具有与 pg_depend 和
 * pg_description 中相同的表示法，但请注意我们将 SUBID 限制为 16 位。
 * 此外，我们使用 DB OID = 0 用于共享对象，如表空间。
 */
#define SET_LOCKTAG_OBJECT(locktag,dboid,classoid,objoid,objsubid) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = (classoid), \
	 (locktag).locktag_field3 = (objoid), \
	 (locktag).locktag_field4 = (objsubid), \
	 (locktag).locktag_type = LOCKTAG_OBJECT, \
	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)

#define SET_LOCKTAG_ADVISORY(locktag,id1,id2,id3,id4) \
	((locktag).locktag_field1 = (id1), \
	 (locktag).locktag_field2 = (id2), \
	 (locktag).locktag_field3 = (id3), \
	 (locktag).locktag_field4 = (id4), \
	 (locktag).locktag_type = LOCKTAG_ADVISORY, \
	 (locktag).locktag_lockmethodid = USER_LOCKMETHOD)


/*
 * 针对每个锁定对象的锁信息：
 *
 * tag -- 唯一标识被锁定的对象
 * grantMask -- 当前在该对象上授予的所有锁类型的位掩码。
 * waitMask -- 当前在该对象上等待的所有锁类型的位掩码。
 * procLocks -- 当前锁的 PROCLOCK 对象列表。
 * waitProcs -- 等待此锁的进程队列。
 * requested -- 当前请求的每种锁类型的计数
 *		（包括已授予的请求！！）。
 * nRequested -- 所有类型总共请求的锁数量。
 * granted -- 当前在该锁上授予的每种锁类型的计数。
 * nGranted -- 所有类型总共授予的锁数量。
 *
 * 注意：这些计数为每个后端计数 1。对于一个后端内部，
 * 可能会在特定锁上有多个抓取，但这不会反映
 * 在共享内存中。
 */
typedef struct LOCK
{
	/* 哈希键 */
	LOCKTAG		tag;			/* 可锁对象的唯一标识符 */

	/* 数据 */
	LOCKMASK	grantMask;		/* 已授予的锁类型的位掩码 */
	LOCKMASK	waitMask;		/* 等待的锁类型的位掩码 */
	SHM_QUEUE	procLocks;		/* 与锁相关的 PROCLOCK 对象列表 */
	PROC_QUEUE	waitProcs;		/* 等待锁的 PGPROC 对象列表 */
	int			requested[MAX_LOCKMODES];	/* 请求锁的计数 */
	int			nRequested;		/* 请求[] 数组的总数 */
	int			granted[MAX_LOCKMODES]; /* 授予锁的计数 */
	int			nGranted;		/* 授予[] 数组的总数 */
} LOCK;

#define LOCK_LOCKMETHOD(lock) ((LOCKMETHODID) (lock).tag.locktag_lockmethodid)
#define LOCK_LOCKTAG(lock) ((LockTagType) (lock).tag.locktag_type)


/*
 * 我们可能有几个不同的后端在同一可锁对象上持有或等待锁。
 * 我们需要为每个这样的持有者（或希望成为持有者）存储一些每个持有者/等待者的信息。
 * 这些信息保存在一个 PROCLOCK 结构中。
 *
 * PROCLOCKTAG 是在 proclock 哈希表中查找 PROCLOCK 项所需的关键信息。
 * 一个 PROCLOCKTAG 值唯一标识可锁对象和该对象的持有者/等待者的组合。
 * （我们可以在此使用指针，因为 PROCLOCKTAG 只需在 PROCLOCK 的生命周期内唯一，
 * 并且它永远不会超出锁或进程的生命周期。）
 *
 * 在后端内部，同一锁可以用于不同的目的：
 * 后端分别跟踪事务锁和会话锁。
 * 然而，这并不会反映在共享内存状态中：
 * 我们只跟踪持有锁的后端。这样做是可以的，因为后端
 * 永远不能阻塞自己。
 *
 * holdMask 字段显示由此 proclock 表示的已授予锁。
 * 注意，对于进程当前等待的任何锁，将会有一个 proclock 对象，
 * 可能其 holdMask 为零。否则，holdMasks 为零的 proclock 对象将在
 * 合适时被回收。
 *
 * releaseMask 是 LockReleaseAll() 的工作区：它显示
 * 在当前调用中即将释放的锁。只能由拥有 PROCLOCK 的后端
 * 查看或设置。
 *
 * 每个 PROCLOCK 对象都被链接到与之关联的 LOCK
 * 对象和拥有的 PGPROC 对象的列表中。注意，PROCLOCK 在创建后
 * 立即被加入这些列表，即使尚未授予任何锁。
 * 等待锁被授予的 PGPROC 也将链接到锁的 waitProcs 队列中。
 */
typedef struct PROCLOCKTAG
{
	/* 注意：我们假设此结构没有填充！ */
	LOCK	   *myLock;			/* 链接到每个可锁对象的信息 */
	PGPROC	   *myProc;			/* 链接到拥有的后端的 PGPROC */
} PROCLOCKTAG;

typedef struct PROCLOCK
{
	/* tag */
	PROCLOCKTAG tag;			/* proclock 对象的唯一标识符 */

	/* 数据 */
	PGPROC	   *groupLeader;	/* 进程的锁组领导者，或进程本身 */
	LOCKMASK	holdMask;		/* 当前持有的锁类型的位掩码 */
	LOCKMASK	releaseMask;	/* 即将释放的锁类型的位掩码 */
	SHM_QUEUE	lockLink;		/* 在 LOCK 的 proclocks 列表中的链接 */
	SHM_QUEUE	procLink;		/* PGPROC 中的 proclocks 列表链接 */
} PROCLOCK;

#define PROCLOCK_LOCKMETHOD(proclock) \
	LOCK_LOCKMETHOD(*((proclock).tag.myLock))

/*
 * 每个后端还维护一个本地哈希表，包含关于每个它当前感兴趣的锁的信息。
 * 特别地，本地表计算该锁被获取的次数。这允许对同一锁的多个请求在不额外访问共享内存的情况下执行。
 * 我们还跟踪每个 ResourceOwner 的锁获取数量，以便可以释放属于特定 ResourceOwner 的那些锁。
 *
 * 当持有“正常”获取的锁时，锁和 proclock 字段始终指向共享内存中的关联对象。
 * 然而，如果我们通过快速通道机制获取了锁，则锁和 proclock 字段被设置为 NULL，因为共享内存中可能没有这些对象。
 * （如果锁后来提升到正常表示，我们可能最终会在找到共享对象后更新我们的 locallock 的锁/proclock 字段。）
 *
 * 警告：locallock 对象可能是由于锁获取尝试失败而遗留的。在这种情况下，它的锁/proclock 字段是不可靠的，因为共享锁对象既未被持有也未被等待，因此可以被回收。
 * 如果 nLocks > 0，则这些指针必须有效或为 NULL，但当 nLocks == 0 时应被视为垃圾。
 */
typedef struct LOCALLOCKTAG
{
	LOCKTAG		lock;			/* 标识可锁定对象 */
	LOCKMODE	mode;			/* 此表项的锁模式 */
} LOCALLOCKTAG;

typedef struct LOCALLOCKOWNER
{
	/*
	 * 注意：如果 owner 为 NULL，则锁是代表会话持有的；
	 * 否则，它是代表我当前的事务持有的。
	 *
	 * 必须使用前向结构引用以避免循环。
	 */
	struct ResourceOwnerData *owner;
	int64		nLocks;			/* 此所有者持有的次数 */
} LOCALLOCKOWNER;

typedef struct LOCALLOCK
{
	/* tag */
	LOCALLOCKTAG tag;			/* locallock 条目的唯一标识符 */

	/* 数据 */
	uint32		hashcode;		/* LOCKTAG 的哈希值副本 */
	LOCK	   *lock;			/* 关联的 LOCK 对象（如果有） */
	PROCLOCK   *proclock;		/* 关联的 PROCLOCK 对象（如果有） */
	int64		nLocks;			/* 锁被持有的总次数 */
	int			numLockOwners;	/* 相关的 ResourceOwners 数量 */
	int			maxLockOwners;	/* 数组的分配大小 */
	LOCALLOCKOWNER *lockOwners; /* 动态可调整大小的数组 */
	bool		holdsStrongLockCount;	/* 提高的 FastPathStrongRelationLocks */
	bool		lockCleared;	/* 我们读取所有 sinval 消息以获取锁 */
} LOCALLOCK;

#define LOCALLOCK_LOCKMETHOD(llock) ((llock).tag.lock.locktag_lockmethodid)
#define LOCALLOCK_LOCKTAG(llock) ((LockTagType) (llock).tag.lock.locktag_type)


/*
 * 这些结构持有从 lmgr 内部传递到锁列出用户级函数（在 lockfuncs.c 中）的信息。
 */

typedef struct LockInstanceData
{
	LOCKTAG		locktag;		/* 被锁定对象的标签 */
	LOCKMASK	holdMask;		/* 此 PGPROC 持有的锁 */
	LOCKMODE	waitLockMode;	/* 此 PGPROC 等待的锁（如果有） */
	BackendId	backend;		/* 此 PGPROC 的后端 ID */
	LocalTransactionId lxid;	/* 此 PGPROC 的本地事务 ID */
	TimestampTz waitStart;		/* 此 PGPROC 开始等待锁的时间 */
	int			pid;			/* 此 PGPROC 的 pid */
	int			leaderPid;		/* 组长的 pid；如果没有组，则 = pid */
	bool		fastpath;		/* 通过快速路径获取？ */
} LockInstanceData;

typedef struct LockData
{
	int			nelements;		/* 数组的长度 */
	LockInstanceData *locks;	/* 每个 PROCLOCK 信息的数组 */
} LockData;

typedef struct BlockedProcData
{
	int			pid;			/* 被阻塞的 PGPROC 的 pid */
	/* 关于 pid 等待的锁的 PROCLOCK 的每个 PROCLOCK 信息 */
	/* （这些字段指的是 BlockedProcsData.locks[] 中的索引） */
	int			first_lock;		/* 第一个相关 LockInstanceData 的索引 */
	int			num_locks;		/* 相关 LockInstanceDatas 的数量 */
	/* 在锁的等待队列中“pid”之前的 PGPROCs 的 PIDs */
	/* (这些字段指的是在 BlockedProcsData.waiter_pids[] 中的索引) */
	int			first_waiter;	/* 第一个前置等待者的索引 */
	int			num_waiters;	/* 前置等待者的数量 */
} BlockedProcData;

typedef struct BlockedProcsData
{
	BlockedProcData *procs;		/* 每个被阻塞的进程信息的数组 */
	LockInstanceData *locks;	/* 每个 PROCLOCK 信息的数组 */
	int		   *waiter_pids;	/* 其他被阻塞的 PGPROCS 的 PIDs 数组 */
	int			nprocs;			/* procs[] 数组中的有效条目数量 */
	int			maxprocs;		/* procs[] 数组的分配长度 */
	int			nlocks;			/* locks[] 数组中的有效条目数量 */
	int			maxlocks;		/* locks[] 数组的分配长度 */
	int			npids;			/* waiter_pids[] 数组中的有效条目数量 */
	int			maxpids;		/* waiter_pids[] 数组的分配长度 */
} BlockedProcsData;


/* LockAcquire() 的结果代码 */
typedef enum
{
	LOCKACQUIRE_NOT_AVAIL,		/* 锁不可用，并且 dontWait=true */
	LOCKACQUIRE_OK,				/* 锁成功获得 */
	LOCKACQUIRE_ALREADY_HELD,	/* 已持有锁的计数增量 */
	LOCKACQUIRE_ALREADY_CLEAR	/* 已清除锁的计数增量 */
} LockAcquireResult;

/* DeadlockCheck() 确定的死锁状态 */
typedef enum
{
	DS_NOT_YET_CHECKED,			/* 尚未运行死锁检查 */
	DS_NO_DEADLOCK,				/* 未检测到死锁 */
	DS_SOFT_DEADLOCK,			/* 通过队列重排列避免的死锁 */
	DS_HARD_DEADLOCK,			/* 死锁，无法逃脱，结果为 ERROR */
	DS_BLOCKED_BY_AUTOVACUUM	/* 无死锁；队列因自动真空工人而被阻塞 */
} DeadLockState;

/*
 * lockmgr 的共享哈希表被分割以减少争用。
 * 要确定给定的 locktag 属于哪个分区，计算该标签的
 * 哈希代码与 LockTagHashCode()，然后应用这些宏之一。
 * 注意：NUM_LOCK_PARTITIONS 必须是 2 的幂！
 */
#define LockHashPartition(hashcode) \
	((hashcode) % NUM_LOCK_PARTITIONS)
#define LockHashPartitionLock(hashcode) \
	(&MainLWLockArray[LOCK_MANAGER_LWLOCK_OFFSET + \
		LockHashPartition(hashcode)].lock)
#define LockHashPartitionLockByIndex(i) \
	(&MainLWLockArray[LOCK_MANAGER_LWLOCK_OFFSET + (i)].lock)

/*
 * 死锁探测器需要能够访问 PGPROC 中的 lockGroupLeader 和
 * 相关字段，因此我们安排那些字段受其中一个锁哈希分区锁保护。
 * 由于死锁探测器无论如何都会获得所有这些锁，因此在不额外
 * 做任何事情的情况下访问这些字段是安全的。为了尽可能
 * 避免争用，我们将不同的 PGPROCs 映射到不同的分区锁。
 * 给定锁组使用的锁由组领导者的 pgprocno 确定。
 */
#define LockHashPartitionLockByProc(leader_pgproc) \
	LockHashPartitionLock((leader_pgproc)->pgprocno)

/*
 * 函数原型
 */
extern void InitLocks(void);
extern LockMethod GetLocksMethodTable(const LOCK *lock);
extern LockMethod GetLockTagsMethodTable(const LOCKTAG *locktag);
extern uint32 LockTagHashCode(const LOCKTAG *locktag);
extern bool DoLockModesConflict(LOCKMODE mode1, LOCKMODE mode2);
extern LockAcquireResult LockAcquire(const LOCKTAG *locktag,
									 LOCKMODE lockmode,
									 bool sessionLock,
									 bool dontWait);
extern LockAcquireResult LockAcquireExtended(const LOCKTAG *locktag,
											 LOCKMODE lockmode,
											 bool sessionLock,
											 bool dontWait,
											 bool reportMemoryError,
											 LOCALLOCK **locallockp);
extern void AbortStrongLockAcquire(void);
extern void MarkLockClear(LOCALLOCK *locallock);
extern bool LockRelease(const LOCKTAG *locktag,
						LOCKMODE lockmode, bool sessionLock);
extern void LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks);
extern void LockReleaseSession(LOCKMETHODID lockmethodid);
extern void LockReleaseCurrentOwner(LOCALLOCK **locallocks, int nlocks);
extern void LockReassignCurrentOwner(LOCALLOCK **locallocks, int nlocks);
extern bool LockHeldByMe(const LOCKTAG *locktag, LOCKMODE lockmode);
extern bool LockOrStrongerHeldByMe(const LOCKTAG *locktag, LOCKMODE lockmode);
#ifdef USE_ASSERT_CHECKING
extern HTAB *GetLockMethodLocalHash(void);
#endif
extern bool LockHasWaiters(const LOCKTAG *locktag,
						   LOCKMODE lockmode, bool sessionLock);
extern VirtualTransactionId *GetLockConflicts(const LOCKTAG *locktag,
											  LOCKMODE lockmode, int *countp);
extern void AtPrepare_Locks(void);
extern void PostPrepare_Locks(TransactionId xid);
extern bool LockCheckConflicts(LockMethod lockMethodTable,
							   LOCKMODE lockmode,
							   LOCK *lock, PROCLOCK *proclock);
extern void GrantLock(LOCK *lock, PROCLOCK *proclock, LOCKMODE lockmode);
extern void GrantAwaitedLock(void);
extern void RemoveFromWaitQueue(PGPROC *proc, uint32 hashcode);
extern Size LockShmemSize(void);
extern LockData *GetLockStatusData(void);
extern BlockedProcsData *GetBlockerStatusData(int blocked_pid);

extern xl_standby_lock *GetRunningTransactionLocks(int *nlocks);
extern const char *GetLockmodeName(LOCKMETHODID lockmethodid, LOCKMODE mode);

extern void lock_twophase_recover(TransactionId xid, uint16 info,
								  void *recdata, uint32 len);
extern void lock_twophase_postcommit(TransactionId xid, uint16 info,
									 void *recdata, uint32 len);
extern void lock_twophase_postabort(TransactionId xid, uint16 info,
									void *recdata, uint32 len);
extern void lock_twophase_standby_recover(TransactionId xid, uint16 info,
										  void *recdata, uint32 len);

extern DeadLockState DeadLockCheck(PGPROC *proc);
extern PGPROC *GetBlockingAutoVacuumPgproc(void);
extern void DeadLockReport(void) pg_attribute_noreturn();
extern void RememberSimpleDeadLock(PGPROC *proc1,
								   LOCKMODE lockmode,
								   LOCK *lock,
								   PGPROC *proc2);
extern void InitDeadLockChecking(void);

extern int	LockWaiterCount(const LOCKTAG *locktag);

#ifdef LOCK_DEBUG
extern void DumpLocks(PGPROC *proc);
extern void DumpAllLocks(void);
#endif

/* 锁定 VXID （用于等待事务完成） */
extern void VirtualXactLockTableInsert(VirtualTransactionId vxid);
extern void VirtualXactLockTableCleanup(void);
extern bool VirtualXactLock(VirtualTransactionId vxid, bool wait);

#endif							/* LOCK_H_ */
